/*
 *
 * Copyright (c) 2008, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Portions of the code may be copied from Google Collections
 * or Apache Commons projects.
 */

package com.coury.dasle;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Set;

import com.coury.dasle.helper.ExceptionHelper;
import com.coury.dasle.helper.FileHelper;
import com.coury.dasle.helper.StringHelper;
import com.coury.dasle.parser.LineParser;
import com.coury.dasle.parser.Parser;
import com.coury.dasle.parser.states.ParseClassNameState;
import com.coury.dasle.parser.states.State;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

public class Dasle {
	
	private static final String COMMENT_PREFIX = "#";
	private final Set<Class<? extends Parser>> registeredParsers = Sets.newHashSet();
	private CommentCallback commentCallback;
	
	private boolean debug = false;
	
	public static Dasle newInstance() {
		return new Dasle();
	}
	
	private Dasle() {
		super();
	}
	
	public void registerParser(Class<? extends Parser> parserClass) {
		registeredParsers.add(parserClass);
	}
	
	public void registerParsers(Class<? extends Parser>... parserClasses) {
		for (Class<? extends Parser> parserClass : parserClasses) {
			registeredParsers.add(parserClass);
		}
	}
	
	public void executeFromResource(String resource) throws Exception {
		BufferedReader reader = new BufferedReader(
				new InputStreamReader(
						getClass().getClassLoader().getResourceAsStream(resource)));

		process(FileHelper.readAsStringList(reader));
	}
	
	public void executeFile(String file) throws Exception {
		process(FileHelper.readAsStringList(new File(file)));
	}

	private void process(List<String> lines) throws Exception {
		for (String line : lines) {
			if (StringHelper.isEmpty(line)) {
				continue;
			}
			
			boolean isComment = line.trim().startsWith(COMMENT_PREFIX); 
			
			if (isComment) {
				if (commentCallback == null || commentCallback.callback(line)) {
					continue;
				}
			}
			else {
				execute(line);
			}
		}
	}

	public void execute(String line) throws Exception {
		if (debug) {
			System.out.println(line);
		}
		for (Class<? extends Parser> p : registeredParsers) {
			if (parse(line, p)) {
				return;
			}
		}

		throw ExceptionHelper.getParseException(
				String.format("Unknown parser for line: %s", line));
	}

	private boolean parse(String line, Class<? extends Parser> parserClass) 
			throws IllegalArgumentException, InstantiationException, 
			IllegalAccessException, InvocationTargetException {
		State currentState = new ParseClassNameState();
		List<String> lineParts = Lists.newArrayList(line.split("\\s"));
		LineParser parserHolder = new LineParser(lineParts);
		
		while (currentState != null) {
			currentState = currentState.execute(parserHolder, parserClass);
		}
		
		if (!parserHolder.isParsed()) {
			return false;
		}
		
		parserHolder.getParser().execute();
		return true;
	}

	public CommentCallback getCommentCallback() {
		return commentCallback;
	}

	public void setCommentCallback(CommentCallback commentCallback) {
		this.commentCallback = commentCallback;
	}

	public boolean isDebug() {
		return debug;
	}

	public void setDebug(boolean debug) {
		this.debug = debug;
	}
}